﻿// <copyright file="Freezable.cs" company="Axelerate Solutions S.A.">
// Copyright (c) Axelerate Solutions S.A.  All rights reserved.
// </copyright>

namespace Axelerate.Silverlight3D.Media
{
    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.ComponentModel;

    /// <summary>
    /// File name: Freezable.cs
    /// Company: Axelerate Solutions S.A.
    /// Created By: Daniel Rojas
    /// Created Date: 10/06/2008 15:14:10 h.
    /// Description: Defines an object that has a modifiable state and a read-only (frozen) state. Classes that derive from Freezable provide detailed change notification, can be made immutable, and can clone themselves.
    ///   <Modifications>
    ///   I first inherit this class from DependencyObject, but i found that reference objects by Name in xaml works better if i inherit from FrameworkElement.
    ///   </Modifications>
    /// </summary>
    public abstract class Freezable : FrameworkElement, INotifyPropertyChanged
    {
        #region Events
        /// <summary>
        /// Occurs when the Freezable or an object it contains is modified.
        /// </summary>
        public event EventHandler Changed;
        #endregion

        #region Fields
        
        /// <summary>
        /// Gets a value indicating whether the object is freezed.
        /// </summary>
        private bool mIsFreezed;

        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of a Freezable derived class.
        /// </summary>
        /// <remarks>This is the protected-accessibility constructor of an abstract class.</remarks>
        public Freezable()
            : base()
        {
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets a value that indicates whether the object is currently modifiable.
        /// </summary>
        /// <value>true if the object is frozen and cannot be modified; false if the object can be modified.</value>
        /// <remarks>
        /// Attempting to modify an object when its IsFrozen property is true throws an InvalidOperationException.
        /// 
        /// This property is read-only from the object model perspective. Some of the documentation about Freezable behaviors may mention "sets IsFrozen to true" or similar language when discussing the behavior of other methods of Freezable, but this behavior is happening internally in the class instances, when methods of the instance manipulate the private variables that exist within the abstract class. To set the value of this property, you should call Freeze. This effectively is a one-time operation to change the IsFrozen property from the initial default false state to the true state. There is no available means to set the value back to false. Instead, you could change any deep copy made from the original (see the Clone method). This is by design and is how any derived class should behave when applied to the cases where the Freezable pattern is useful.
        /// </remarks>
        public bool IsFrozen
        {
            get
            {
                return this.mIsFreezed;
            }
        }

        /// <summary>
        /// Gets a value that indicates whether the object can be made unmodifiable.
        /// </summary>
        /// <value>true if the current object can be made unmodifiable or is already unmodifiable; otherwise, false.</value>
        /// <remarks>
        /// This method implementation uses the FreezeCore method with isChecking set to true to determine whether a Freezable can be made unmodifiable. To modify the way this property behaves in a derived class, override the FreezeCore method.
        /// </remarks>
        public bool CanFreeze
        {
            get
            {
                return false;
                // Bellow is the correct implementation. But for know, we don't have time to implement well on inherited clasess.
                ////if (IsFrozen)
                ////{
                ////    return true;
                ////}
                ////else
                ////{
                ////    return FreezeCore(true);
                ////}
            }
        }
        #endregion

        #region Methods

        /// <summary>
        /// Called when the current Freezable object is modified.
        /// </summary>
        protected virtual void OnChanged()
        {
            EventHandler handler = Changed;
            if (handler != null)
            {
                handler(this, null);
            }
        }

        /// <summary>
        /// Makes the Freezable object unmodifiable or tests whether it can be made unmodifiable.
        /// </summary>
        /// <param name="isChecking"></param>
        /// <returns></returns>
        /// <remarks>
        /// Do not call this method directly (except when calling base in an implementation). This method is called internally by the CanFreeze property (with isChecking equal to true) and the Freeze method (with isChecking equal to false).
        /// Freezable implementers must override this method when the class contains data that is not stored using dependency properties.
        /// A typical implementation would call base, then call the static Freeze(Freezable, Boolean) method on all Freezable typed properties that the class contains, returning true only if all properties were frozen (or could have been frozen, in the case of specifying true for isChecking).
        /// </remarks>
        protected virtual bool FreezeCore(bool isChecking)
        {
            if (isChecking)
            {
                if (!IsFrozen)
                {
                    mIsFreezed = true;
                    this.NotifyPropertyChanged("IsFreezed"); 
                }
            }
            return true;
        }

        /// <summary>
        /// Makes the current object unmodifiable and sets its IsFrozen property to true.
        /// </summary>
        /// <exception cref="System.InvalidOperationException">The Freezable cannot be made unmodifiable.</exception>
        public void Freeze()
        {
            if (!this.FreezeCore(false))
            {
                throw new InvalidOperationException();
            }
        }

        /// <summary>
        /// If the isChecking parameter is true, this method indicates whether the specified Freezable can be made unmodifiable. If the isChecking parameter is false, this method attempts to make the specified Freezable unmodifiable and indicates whether the operation succeeded.
        /// </summary>
        /// <param name="freezable">The object to check or make unmodifiable. If isChecking is true, the object is checked to determine whether it can be made unmodifiable. If isChecking is false, the object is made unmodifiable, if possible.</param>
        /// <param name="isChecking">true to return an indication of whether the object can be frozen (without actually freezing it); false to actually freeze the object.</param>
        /// <returns>If isChecking is true, this method returns true if the specified Freezable can be made unmodifiable, or false if it cannot be made unmodifiable. If isChecking is false, this method returns true if the specified Freezable is now unmodifiable, or false if it cannot be made unmodifiable.</returns>
        protected internal static bool Freeze(Freezable freezable, bool isChecking)
        {
            if (isChecking && freezable.CanFreeze)
            {
                return true;
            }
            else if (!isChecking && freezable.CanFreeze)
            {
                return freezable.FreezeCore(isChecking);
            }
            return false;
        }
        #endregion
        
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Called to notify that a property was changed.
        /// </summary>
        /// <param name="propertyName">Name of property changed</param>
        protected virtual void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
            this.OnChanged();
        }
        #endregion

        
    }
}
